<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        html, body {
            margin: 0;
            height: 100%;
        }

        canvas {
            display: block;
        }
        #blocker {
            position: absolute;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
        }
        #instructions {
            width: 100%;
            height: 100%;
            display: -webkit-box;
            display: -moz-box;
            display: box;
            -webkit-box-orient: horizontal;
            -moz-box-orient: horizontal;
            box-orient: horizontal;
            -webkit-box-pack: center;
            -moz-box-pack: center;
            box-pack: center;
            -webkit-box-align: center;
            -moz-box-align: center;
            box-align: center;
            color: #ffffff;
            text-align: center;
            cursor: pointer;
        }

    </style>
</head>

<body onload="draw();">
<div id="blocker">

    <div id="instructions">
        <span style="font-size:40px">点击屏幕开始</span>
        <br />
        <br />
        (W, A, S, D = 移动, SPACE = 跳跃, MOUSE = 移动视角)
    </div>

</div>
</body>
<script src="../src/three.js"></script>
<!-- <script src="../src/OrbitControls.js"></script> -->
<script src="../src/PointerLockControls.js"></script>
<script src="../src/loaders/MTLLoader.js"></script>
<script src="../src/loaders/OBJLoader.js"></script>
<script src="../src/libs/chroma.js"></script>

<script>
    var renderer,camera,scene,gui,light,stats,controls;
    var clock = new THREE.Clock();
    //是否锁定页面的相关
    var blocker = document.getElementById( 'blocker' );
    var instructions = document.getElementById( 'instructions' );
    //移动相关的变量
    var controlsEnabled = false;
    var moveForward = false;
    var moveBackward = false;
    var moveLeft = false;
    var moveRight = false;
    var canJump = false;
    var spaceUp = true; //处理一直按着空格连续跳的问题
    //声明射线
    var upRaycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3( 0, 1, 0), 0, 10);
    var horizontalRaycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(), 0, 10);
    var downRaycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3( 0, -1, 0), 0, 10);

    var velocity = new THREE.Vector3()      // 移动速度变量 记录 x/y/z ( 左右，上下 ) 三个方向上的速度
    var direction = new THREE.Vector3()     // 移动的方向变量
    var rotation = new THREE.Vector3()      // 当前的相机朝向 记录当前相机的世界方向向量

    var speed = 500; //控制器移动速度
    var upSpeed = 200; //控制跳起时的速度

    //辅助箭头
    var up,horizontal,down,group;

    function initRender() {
        renderer = new THREE.WebGLRenderer({antialias:true});
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.sortObjects = false;
        //告诉渲染器需要阴影效果
        document.body.appendChild(renderer.domElement);
    }

    function initCamera() {
        camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
        camera.position.set(0, 10, 50);
    }

    function initScene() {
        scene = new THREE.Scene();
    }

    //初始化dat.GUI简化试验流程
    function initGui() {
        //声明一个保存需求修改的相关数据的对象
        //gui = {};
        //var datGui = new dat.GUI();
        //将设置属性添加到gui当中，gui.add(对象，属性，最小值，最大值）
    }

    function initLight() {
        scene.add(new THREE.AmbientLight(0x444444));

        light = new THREE.PointLight(0xffffff);
        light.position.set(0,50,0);

        //告诉平行光需要开启阴影投射
        light.castShadow = true;

        scene.add(light);
    }

    function initModel() {

        //辅助工具
        var helper = new THREE.AxesHelper(50);
        //scene.add(helper);

        var mtlLoader = new THREE.MTLLoader();
        mtlLoader.setPath('city/');
        //加载mtl文件
        mtlLoader.load('city.mtl', function (material) {
            var objLoader = new THREE.OBJLoader();
            //设置当前加载的纹理
            objLoader.setMaterials(material);
            objLoader.setPath('city/');
            objLoader.load('city.obj', function (object) {
                //设置颜色的取值范围
                var scale = chroma.scale(['yellow', '008ae5']);

                //重新设置纹理颜色
                setRandomColors(object, scale);

                object.scale.set(5, 5, 5);
                //将模型缩放并添加到场景当中
                scene.add(object);
            })
        });

        //添加辅助线
        // group = new THREE.Group();
        // up = new THREE.ArrowHelper(new THREE.Vector3(0, 1, 0), new THREE.Vector3(), 10, 0x00ff00);
        // horizontal = new THREE.ArrowHelper(new THREE.Vector3(1, 0, 0), new THREE.Vector3(), 10, 0x00ffff);
        // down = new THREE.ArrowHelper(new THREE.Vector3(0, -1, 0), new THREE.Vector3(), 10, 0xffff00);

        // group.add(up);
        // group.add(horizontal);
        // group.add(down);

        // scene.add(group);
    }

    //添加纹理的方法
    function setRandomColors(object, scale) {
        //获取children数组
        var children = object.children;

        //如果当前模型有子元素，则遍历子元素
        if (children && children.length > 0) {
            children.forEach(function (e) {
                setRandomColors(e, scale)
            });
        }
        else {
            if (object instanceof THREE.Mesh) {
                //如果当前的模型是楼层，则设置固定的颜色，并且透明化
                if(Array.isArray(object.material)){
                    for(var i = 0; i<object.material.length; i++){
                        var material = object.material[i];
                        var color = scale(Math.random()).hex();
                        if (material.name.indexOf("building") === 0) {
                            material.color = new THREE.Color(color);
                            material.transparent = true;
                            material.opacity = 0.7;
                            material.depthWrite = false;
                        }
                    }
                }
                // 如果不是场景组，则给当前mesh添加纹理
                else{
                    //随机当前模型的颜色
                    object.material.color = new THREE.Color(scale(Math.random()).hex());
                }
            }
        }
    }

    function initControls() {

        controls = new THREE.PointerLockControls( camera );
        controls.getObject().position.y = 50    // 设置相机的初始位置  
        controls.getObject().position.x = 100
        scene.add( controls.getObject() )
        var onKeyDown = function ( event ) {    // 开启相应的移动动作
            switch ( event.keyCode ) {
                case 38: // up
                case 87: // w
                    moveForward = true;
                    break;
                case 37: // left
                case 65: // a
                    moveLeft = true; break;
                case 40: // down
                case 83: // s
                    moveBackward = true;
                    break;
                case 39: // right
                case 68: // d
                    moveRight = true;
                    break;
                case 32: // space
                    if ( canJump && spaceUp ) velocity.y += upSpeed;
                    canJump = false;
                    spaceUp = false;
                    break;
            }
        };
        var onKeyUp = function ( event ) {  // 关闭相应的移动动作
            switch( event.keyCode ) {
                case 38: // up
                case 87: // w
                    moveForward = false;
                    break;
                case 37: // left
                case 65: // a
                    moveLeft = false;
                    break;
                case 40: // down
                case 83: // s
                    moveBackward = false;
                    break;
                case 39: // right
                case 68: // d
                    moveRight = false;
                    break;
                case 32: // space
                    spaceUp = true;
                    break;
            }
        };
        document.addEventListener( 'keydown', onKeyDown, false );
        document.addEventListener( 'keyup', onKeyUp, false );
    }

    function initPointerLock() {
        //实现鼠标锁定的教程地址 http://www.html5rocks.com/en/tutorials/pointerlock/intro/
        var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
        if ( havePointerLock ) {
            var element = document.body;
            var pointerlockchange = function ( event ) {
                if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {
                    controlsEnabled = true;
                    controls.enabled = true;
                    blocker.style.display = 'none';
                } else {
                    controls.enabled = false;
                    blocker.style.display = 'block';
                    instructions.style.display = '';
                }
            };
            var pointerlockerror = function ( event ) {
                instructions.style.display = '';
            };
            // 监听变动事件
            document.addEventListener( 'pointerlockchange', pointerlockchange, false );
            document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
            document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );
            document.addEventListener( 'pointerlockerror', pointerlockerror, false );
            document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
            document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );
            instructions.addEventListener( 'click', function ( event ) {
                instructions.style.display = 'none';
                //全屏
                launchFullScreen(renderer.domElement);
                // 锁定鼠标光标
                element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
                element.requestPointerLock();
            }, false );
        }
        else {
            instructions.innerHTML = '你的浏览器不支持相关操作，请更换浏览器';
        }
    }

    function render() {
        if ( controlsEnabled === true ) {
            //获取到控制器对象
            var control = controls.getObject();
            //获取刷新时间
            var delta = clock.getDelta();

            //获取当前按键的方向并获取朝哪个方向移动
            direction.z = Number( moveForward ) - Number( moveBackward )    // Number() 函数将布尔值转换为数字 Number(true) = 1, Number(false) = 0
            direction.x = Number( moveLeft ) - Number( moveRight )
            // direction 在 y 轴上的值默认为 0
            //将法向量的值归一化 ( 获得水平方向的单位法向量 )
            direction.normalize()

            // 辅助线的位置
            // group.position.set(control.position.x,control.position.y,control.position.z)

            //判断是否接触到了模型

            // rotation.copy(control.getWorldDirection(new THREE.Vector3()).multiply(new THREE.Vector3(-1, 0, -1)))
            rotation.copy(control.getWorldDirection(new THREE.Vector3()))  // multiply(new THREE.Vector3(1, 0, 1))的作用是取消 y 轴方向上的速度
            // if(rotation.y > 0){//.multiply(new THREE.Vector3(1, 0, 1))
            //     console.log(rotation)
            //     rotation.y = 0
            // }
            //判断鼠标按下的方向 得到相机应该转动的变换矩阵
            var m = new THREE.Matrix4()
            if(direction.z > 0){
                if(direction.x > 0){
                    m.makeRotationY(Math.PI/4);
                }
                else if(direction.x < 0){
                    m.makeRotationY(-Math.PI/4);
                }
                else{
                    m.makeRotationY(0);
                }
            }
            else if(direction.z < 0){
                if(direction.x > 0){
                    m.makeRotationY(Math.PI/4*3);
                }
                else if(direction.x < 0){
                    m.makeRotationY(-Math.PI/4*3);
                }
                else{
                    m.makeRotationY(Math.PI);
                }
            }
            else{
                if(direction.x > 0){
                    m.makeRotationY(Math.PI/2);
                }
                else if(direction.x < 0){
                    m.makeRotationY(-Math.PI/2);
                }
            }
            //给向量使用变换矩阵
            rotation.applyMatrix4(m)
            // horizontal.setDirection(rotation)
           
            horizontalRaycaster.set( control.position , rotation )  // 更新水平射线
            // horizontalRaycaster.set( control.position , rotation.multiply(new THREE.Vector3(-1, 0, -1)) )

            var horizontalIntersections = horizontalRaycaster.intersectObjects( scene.children, true)   // 判断当前位置水平射线的物体相交检测
            var horOnObject = horizontalIntersections.length > 0;   // 水平( 前方 0-10 的范围内出现 object )
            // 虽然只是前方的检测 但是侧面同样可以起到效果
            
            // 判断移动方向修改速度方向 为移动方向赋予相应的速度
            if(!horOnObject){ 
                if ( moveForward || moveBackward ){ // 因为 z 轴的正方向垂直屏幕向外，所以用减法
                    velocity.z -= direction.z * speed * delta   // 用 -= 是为了体现加速度
                } 
                if ( moveLeft || moveRight ) {
                    velocity.x -= direction.x * speed * delta
                } 
            }
            

            // velocity每次的速度，为了保证有过渡 时刻保持
            velocity.x -= velocity.x * 10.0 * delta // 
            velocity.z -= velocity.z * 10.0 * delta
            velocity.y -= 9.8 * 100.0 * delta; // 默认下降的速度

            

            if(canJump){    // 当相机下降到地面时下降速度为0
                velocity.y = 0
            }
            if(control.position.y > 10 && canJump){
                control.position.y = 0
            }
            
            // //复制相机的位置
            // downRaycaster.ray.origin.copy( control.position )
            // //获取相机靠下10的位置
            // downRaycaster.ray.origin.y -= 10
            // //判断是否停留在了立方体上面
            // var intersections = downRaycaster.intersectObjects( scene.children, true)
            // var onObject = intersections.length > 0
            // //判断是否停在了立方体上面
            // if ( onObject === true ) {
            //     velocity.y = Math.max( 0, velocity.y );
            //     canJump = true;
            // }

            //根据速度值移动控制器
            control.translateX( velocity.x * delta );
            control.translateY( velocity.y * delta );
            control.translateZ( velocity.z * delta );

            //保证控制器的y轴在10以上
            if ( control.position.y <= 10 ) {
                velocity.y = 0;
                control.position.y = 10;
                canJump = true;
            }
        }

    }

    //窗口变动触发的函数
    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        render();
        renderer.setSize( window.innerWidth, window.innerHeight );
    }

    function animate() {
        //更新控制器
        render()
        renderer.render( scene, camera )
        requestAnimationFrame(animate)
    }

    function draw() {
        initPointerLock();
        // initGui();
        initScene();
        initCamera();
        initRender();
        initLight();
        initModel();
        initControls();

        animate();
        window.onresize = onWindowResize;
    }

    function launchFullScreen(element) {
        /*if (element.requestFullscreen) {
            element.requestFullscreen();
        }
        else if (element.mozRequestFullScreen) {
            element.mozRequestFullScreen();
        }
        else if (element.webkitRequestFullscreen) {
            element.webkitRequestFullscreen();
        }
        else if (element.msRequestFullscreen) {
            element.msRequestFullscreen();
        }*/
    }

</script>
</html>